﻿using System;
using System.Collections.Generic;
using System.Windows.Media;

using Bling.Core;
using Bling.DSL;
using Bling.WPF;
using Bling.Graphics;
using Bling.DX;
using Bling.Matrices;
using Bling.WPF3D;
using Bling.Angles;

namespace Bling.Example {
  public class BasicLight : HasDX10Canvas {
    public BasicLight()
      // by passing true to base, an overlay window will be created.
      : base(true) {
      Host.Size = this.Bl().Width.Min(this.Bl().Height); 
      Host.LeftTop = 0d;
      // background of overlay can't be transparent or it won't receive input. It can be a very transparent color though, go fig. 
      OverlayContent.Background = new SolidColorBrushBl() { 
        Color = Colors.Red, 
        Opacity = OverlayContent.IsMouseOver.Condition(.05d, .01d), 
      };
      // compute virtual sphere mouse location according to mouse's 2D position
      Double2Bl MouseAt = OverlayContent.Mouse.Position / OverlayContent.Size;
      // screen cooridnates are [0,1] while 3D coordinates are [-1,1], use n * 2d - 1 to translate.
      MouseAt = MouseAt * 2d - 1d;
      // for some reason, Y is flipped in result, easy enough to fix. 
      MouseAt = MouseAt * new Double2Bl(+1d, -1d);
      Double2Bl LightPosition = this.Property(Double2Bl.Default);
      LightPosition[OverlayContent.Mouse.Drag.Update] = 
        LightPosition[OverlayContent.Mouse.Drag.In] + (MouseAt - MouseAt[OverlayContent.Mouse.Drag.In]);
      FaceMesh Face = new FaceMesh();

      //  create a perspective camera.
      Double3Bl WorldCenter = new Double3Bl(0, 0, 0);
      // camera position is used in lighting, so externalize
      Double3Bl CameraPosition = WorldCenter + new Double3Bl(0, 0, -4);
      
      CameraCl Camera = new PerspectiveCameraCl() {
        Position = CameraPosition,
      };
      Device.Render(Face, (Mesh3D<FaceMesh>.Mesh3DEntry Entry, IVertex vertex) => {
        // find the xyz coordinate of the current vertex index
        Double3Bl WorldPos = Entry.Position;
        WorldPos = ((Camera.Project * Camera.View)).Transform(WorldPos);
        vertex.Position = WorldPos;

        ColorBl clr = new ColorBl[] {
          Colors.Blue, Colors.Red, Colors.Blue, Colors.Red,
        }.Table(Entry.Index);

        Double3Bl normal = Entry.Normal;
        
        // illuminate the cubes with a point light on a diffuse material.
        LightCl light = new SpotLightCl() {
          Direction = (new Double3Bl(0, 0, +1)).Normalize,
          Color = Colors.White,
          Position = new Double3Bl(LightPosition, -10),
          InnerConeAngle = 2.Degrees(),
          OuterConeAngle = 4.Degrees(),
        };
        MaterialCl diffuse = new DiffuseMaterialCl() {
          // how much non-ambient light is relfected
          Knob = .5,
          Ambient = {
            // how much ambient light is reflected
            Knob = .5,
          }
        };
        // combine light with diffuse material via apply, plugin in world position, normal, and camera position, then color. 
        // also, compose an ambient light so nothing is completely dark.
        // Note: 
        // Use WorldPos.PerPixel() rather than WorldPos for better per-pixel lighting computations.
        // If efficiency is an issue, use WorldPos instead for cheaper per-vertex lighting computations.
        clr = diffuse.ApplyLights(light + new AmbientLightBl() { Color = Colors.White }).
          ApplyAt(WorldPos.PerPixel(), normal, CameraPosition).ForColor(clr);
        vertex.Color = clr;
      });
    }
  }
}
